home *** CD-ROM | disk | FTP | other *** search
/ Amiga Developer CD 2.1 / Amiga Developer CD v2.1.iso / Extras / Networking / SANA-II / slip_src / odu / source / OwnDevUnit.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-02-17  |  26.9 KB  |  928 lines

  1. /* OwnDevUnit.c
  2.  
  3.    Copyright 1991 by Christopher A. Wichura (caw@miroc.chi.il.us)
  4.    All Rights Reserved.
  5.  
  6.    This implements a fairly simple blocking mechanism for a device/unit
  7.    specification.
  8.  
  9.    Note that this probably won't compile unless using SAS 6.02.
  10.    It also assumes KickStart 3.0 include files.
  11.  
  12.    This code is freely redistributable.  You may not charge anything
  13.    for it, except for media and/or postage.  In using this code, you
  14.    assume all responsibility for any damage, loss of productivity/money,
  15.    or other disaster that occurs while this code is in use.  C. Wichura
  16.    can not, and will not, be held responsible.
  17. */
  18.  
  19. #include <exec/types.h>
  20. #include <exec/lists.h>
  21. #include <exec/nodes.h>
  22. #include <exec/memory.h>
  23. #include <exec/ports.h>
  24. #include <exec/tasks.h>
  25. #include <exec/libraries.h>
  26. #include <exec/semaphores.h>
  27. #include <exec/execbase.h>
  28. #include <devices/timer.h>
  29. #include <rexx/errors.h>
  30.  
  31. #include <clib/exec_protos.h>
  32. extern struct ExecBase *SysBase;
  33. #include <pragmas/exec_lib.h>
  34.  
  35. #include <clib/rexxsyslib_protos.h>
  36. extern struct RxsLib *RexxSysBase;
  37. #include <pragmas/rexxsyslib_lib.h>
  38.  
  39. #include <string.h>
  40. #include <stdlib.h>
  41. #include <clib/alib_protos.h>
  42.  
  43. #include <clib/pools_protos.h>
  44.  
  45. LONG __stdargs kprintf(STRPTR fmt, ...);
  46.  
  47. /* we keep a list of all device/unit pairs we are currently tracking.
  48.    a semaphore is used to prevent collisions while accessing this
  49.    list. */
  50. struct SignalSemaphore MasterListSema;
  51. struct MinList MasterList;
  52.  
  53. typedef struct {
  54.     struct MinNode    wn_Node;
  55.     struct Task    *wn_Task;
  56. } WaitListNode_t;
  57.  
  58. typedef struct {
  59.     struct MinNode        mn_Node;
  60.     ULONG            mn_SizeOf;
  61.     struct Task        *mn_OwnerTask;
  62.     ULONG            mn_Unit;
  63.     UBYTE            *mn_OwnerName;
  64.     struct MinList        mn_WaitList;
  65.     UBYTE            mn_NotifyBit;
  66.     UBYTE            mn_Device[1];    /* dynamically allocated */
  67. } MasterListNode_t;
  68.  
  69. /* execbase and pool header for when we are running under v39 or higher */
  70. struct ExecBase *SysBase;
  71. APTR pool;
  72.  
  73. /* as a failsafe for programs that don't check their notifybit properly,
  74.    we will re-signal them every RE_SIGNAL times through the loop that
  75.    waits for the port to become free. */
  76. #define RE_SIGNAL 10
  77.  
  78. /* strings that can be returned by LockDevUnit() and AttempDevUnit() to
  79.    indicate an internal error.  Note that these all start with a
  80.    special character. */
  81.  
  82. #define ODUERR_LEADCHAR "\x07"
  83.  
  84. #define ODUERR_NOMEM    ODUERR_LEADCHAR "Out of memory"
  85. #define ODUERR_NOTIMER    ODUERR_LEADCHAR "Unable to open timer.device"
  86. #define ODUERR_BADNAME    ODUERR_LEADCHAR "Bogus device name supplied"
  87. #define ODUERR_BADBIT    ODUERR_LEADCHAR "Bogus notify bit supplied"
  88. #define ODUERR_UNKNOWN    ODUERR_LEADCHAR "Unknown"
  89.                 /* returned if owner's name is NULL */
  90.  
  91. /* we keep track of our seglist for LibExpunge() */
  92. ULONG LibrarySeg;
  93.  
  94. /* these are some error return values for the ARexx dispatcher */
  95. #define WRONG_NUM_OF_ARGS ERR10_017
  96. #define NO_MEM_FOR_ARGSTRING ERR10_003
  97. #define NO_REXX_LIB ERR10_014
  98.  
  99. /***************************************************************************/
  100. /***************************************************************************/
  101. /**           Here we prototype the functions found in this code          **/
  102. /***************************************************************************/
  103. /***************************************************************************/
  104.  
  105. struct Library * __saveds __asm LibInit(register __d0 struct Library *LibBase,
  106.                                         register __a0 ULONG LibSegment);
  107. struct Library * __saveds __asm LibOpen(register __a6 struct Library *LibraryBase);
  108. ULONG __saveds __asm LibClose(register __a6 struct Library *LibraryBase);
  109. ULONG __saveds __asm LibExpunge(register __a6 struct Library *LibraryBase);
  110.  
  111. UBYTE * __saveds __asm LockDevUnit(register __a0 UBYTE *Device,
  112.     register __d0 ULONG Unit, register __a1 UBYTE *OwnerName,
  113.     register __d1 UBYTE NotifyBit);
  114. UBYTE * __saveds __asm AttemptDevUnit(register __a0 UBYTE *Device,
  115.     register __d0 ULONG Unit, register __a1 UBYTE *OwnerName,
  116.     register __d1 UBYTE NotifyBit);
  117. void __saveds __asm FreeDevUnit(register __a0 UBYTE *Device,
  118.     register __d0 ULONG Unit);
  119. void __saveds __asm NameDevUnit(register __a0 UBYTE *Device,
  120.     register __d0 ULONG Unit, register __a1 UBYTE *OwnerName);
  121. BOOL __saveds __asm AvailDevUnit(register __a0 UBYTE *Device,
  122.     register __d0 ULONG Unit);
  123. UBYTE *__saveds __asm OwnerDevUnit(register __a0 UBYTE *Device,
  124.     register __d0 ULONG Unit);
  125. ULONG __saveds __asm RexxQuery(register __a0 struct RexxMsg *RMsg,
  126.                   register __a1 UBYTE **ReturnArgString);
  127.  
  128. UBYTE *DoDevUnit(UBYTE *Device, ULONG Unit, UBYTE *OwnerName,
  129.     UBYTE NotifyBit, ULONG AttemptMaximum, ULONG AttemptDelay);
  130.  
  131. MasterListNode_t *FindMasterNode(UBYTE *Device, ULONG Unit);
  132. MasterListNode_t *CreateMasterNode(UBYTE *Device, ULONG Unit);
  133. void __inline SetMasterNode(MasterListNode_t *Node, UBYTE *OwnerName, UBYTE NotifyBit);
  134. UBYTE * __inline BaseName(UBYTE *Path);
  135.  
  136. BOOL OpenTimer(struct timerequest *TimeRequest, struct MsgPort *ReplyPort);
  137. void CloseTimer(struct timerequest *TimeRequest, struct MsgPort *ReplyPort);
  138. void __inline Sleep(struct timerequest *TimeRequest, struct MsgPort *ReplyPort, ULONG Seconds);
  139.  
  140. /***************************************************************************/
  141. /***************************************************************************/
  142. /**     These are the library base routines used to open/close us, etc    **/
  143. /***************************************************************************/
  144. /***************************************************************************/
  145.  
  146. struct Library * __saveds __asm LibInit(register __d0 struct Library *LibBase,
  147.                                         register __a0 ULONG LibSegment)
  148. {
  149. #ifdef DEBUG
  150.     kprintf("LibInit called Base = %08lx\tSegment = %08lx\n", LibBase, LibSegment);
  151. #endif
  152.     SysBase = *(struct ExecBase **)4L;
  153.     LibrarySeg = LibSegment;
  154.  
  155.     NewList((struct List *)&MasterList);
  156.     InitSemaphore(&MasterListSema);
  157.  
  158.         if (!(pool = CreatePool(MEMF_PUBLIC | MEMF_CLEAR, 4096, 2048))) {
  159. #ifdef DEBUG
  160.     kprintf("Couldn't create memory pool\n");
  161. #endif
  162.              return 0L;
  163.         }
  164.  
  165.     return LibBase;
  166. }
  167.  
  168. struct Library * __saveds __asm LibOpen(register __a6 struct Library *LibraryBase)
  169. {
  170. #ifdef DEBUG
  171.     kprintf("LibOpen called");
  172. #endif
  173.     LibraryBase->lib_OpenCnt++;
  174.     LibraryBase->lib_Flags &= ~LIBF_DELEXP;
  175.  
  176. #ifdef DEBUG
  177.     kprintf(" OpenCount = %ld\n", LibraryBase->lib_OpenCnt);
  178. #endif
  179.     return LibraryBase;
  180. }
  181.  
  182. ULONG __saveds __asm LibClose(register __a6 struct Library *LibraryBase)
  183. {
  184. #ifdef DEBUG
  185.     kprintf("LibClose called");
  186. #endif
  187.     if (--LibraryBase->lib_OpenCnt == 0)
  188.         if (LibraryBase->lib_Flags & LIBF_DELEXP) {
  189. #ifdef DEBUG
  190.             kprintf("\n");
  191. #endif
  192.             return LibExpunge(LibraryBase);
  193.         }
  194.  
  195. #ifdef DEBUG
  196.     kprintf(" OpenCount = %ld\n", LibraryBase->lib_OpenCnt);
  197. #endif
  198.     return (ULONG) NULL;
  199. }
  200.  
  201. ULONG __saveds __asm LibExpunge(register __a6 struct Library *LibraryBase)
  202. {
  203.     BOOL    KillLib;
  204.     LONG    LibSize;
  205.  
  206. #ifdef DEBUG
  207.     kprintf("LibExpunge called\n");
  208. #endif
  209.  
  210.     KillLib = FALSE;
  211.  
  212.     /* we will refuse to expunge if we are currently tracking devices,
  213.        regardless of our OpenCnt.  We AttemptSemaphore() the MasterList
  214.        before checking it.  If someone else owns it then obviously we
  215.        can't try to shut down. */
  216.  
  217.     if (LibraryBase->lib_OpenCnt == 0 && AttemptSemaphore(&MasterListSema)) {
  218. #ifdef DEBUG
  219.         kprintf("LIBEXP: Checking if MasterList is empty\n");
  220. #endif
  221.         if (MasterList.mlh_TailPred == (struct MinNode *)&MasterList.mlh_Head) {
  222.             Remove((struct Node *)LibraryBase);
  223.             KillLib = TRUE;
  224.         }
  225.  
  226.         ReleaseSemaphore(&MasterListSema);
  227.     }
  228.  
  229.     if (KillLib) {
  230. #ifdef DEBUG
  231.         kprintf("LIBEXP: Removing this library!\n");
  232. #endif
  233.         DeletePool(pool);
  234.  
  235.         LibSize = LibraryBase->lib_NegSize + LibraryBase->lib_PosSize;
  236.         FreeMem((char *)LibraryBase - LibraryBase->lib_NegSize, LibSize);
  237.  
  238.         return LibrarySeg;
  239.     }
  240.  
  241. #ifdef DEBUG
  242.     kprintf("LIBEXP: Can't remove library, setting LIBF_DELEXP\n");
  243. #endif
  244.     LibraryBase->lib_Flags |= LIBF_DELEXP;
  245.     return (ULONG) NULL;
  246. }
  247.  
  248. /***************************************************************************/
  249. /***************************************************************************/
  250. /**       Now we have the routines that do smart memory allocation        **/
  251. /***************************************************************************/
  252. /***************************************************************************/
  253.  
  254.  /*
  255.   * these routines will use pooled memory under V39 or higher or standard
  256.   * memory routines when running under earlier versions of the OS. We lock
  257.   * MasterListSema as a means to prevent multiple people from stomping on the
  258.   * pool handle concurrently.
  259.   */
  260.  
  261. static void *__inline MyAllocVec(ULONG size)
  262. {
  263.     UBYTE *memory;
  264.  
  265.     size += 4;
  266.  
  267.     ObtainSemaphore(&MasterListSema);
  268.     memory = AllocPooled(pool, size);
  269.     ReleaseSemaphore(&MasterListSema);
  270.  
  271.     if (!memory)
  272.         return NULL;
  273.  
  274.     *((ULONG *) memory) = size;
  275.  
  276.     return (void *)(memory + 4);
  277. }
  278.  
  279. static void __inline MyFreeVec(void *memory)
  280. {
  281.     void *realMemory;
  282.     ULONG size;
  283.  
  284.     if (!memory)
  285.         return;
  286.  
  287.     realMemory = (UBYTE *) memory - 4;
  288.     size = *((ULONG *) realMemory);
  289.  
  290.     ObtainSemaphore(&MasterListSema);
  291.     FreePooled(pool, realMemory, size);
  292.     ReleaseSemaphore(&MasterListSema);
  293. }
  294.  
  295. /***************************************************************************/
  296. /***************************************************************************/
  297. /**       Now we have the routines that acutally make up the library      **/
  298. /***************************************************************************/
  299. /***************************************************************************/
  300.  
  301. /* LockDevUnit() will block until it gets hold of the requested
  302.    device and unit
  303. */
  304.  
  305. UBYTE * __saveds __asm LockDevUnit(register __a0 UBYTE *Device,
  306.     register __d0 ULONG Unit, register __a1 UBYTE *OwnerName,
  307.     register __d1 UBYTE NotifyBit)
  308. {
  309.     return DoDevUnit(Device, Unit, OwnerName, NotifyBit, -1L, 3L);
  310. }
  311.  
  312. /* AttemptDevUnit() will try to grab the requested device and unit.
  313.    We allow up to five seconds for an owner of the lock to let it
  314.    go before we return a failure.
  315. */
  316.  
  317. UBYTE *__saveds __asm AttemptDevUnit(register __a0 UBYTE *Device,
  318.     register __d0 ULONG Unit, register __a1 UBYTE *OwnerName,
  319.     register __d1 UBYTE NotifyBit)
  320. {
  321.     return DoDevUnit(Device, Unit, OwnerName, NotifyBit, 5L, 1L);
  322. }
  323.  
  324. /* FreeDevUnit() releases a lock on a device/unit that was previously
  325.    attained by a call to LockDevUnit() or AttempDevUnit()
  326.  
  327.    if there is stuff hanging off the node's waitlist then we just
  328.    null the owner task field so that LockDevUnit() and AttemptDevUnit()
  329.    know that the port is free.  if there isn't anything on the waitlist
  330.    then no one else wants this node so remove the node from the master
  331.    list and free the memory used by it.
  332. */
  333.  
  334. void __saveds __asm FreeDevUnit(register __a0 UBYTE *Device,
  335.     register __d0 ULONG Unit)
  336. {
  337.     MasterListNode_t *Master;
  338.  
  339. #ifdef DEBUG
  340.     kprintf("FreeDevUnit called \"%s\" %ld\n", Device, Unit);
  341. #endif
  342.  
  343.     if (!Device)
  344.         return;
  345.  
  346.     ObtainSemaphore(&MasterListSema);
  347.  
  348.     if (Master = FindMasterNode(Device, Unit)) {
  349. #ifdef DEBUG
  350.         kprintf("FDU: Master = %08lx\n", Master);
  351. #endif
  352.         if (Master->mn_OwnerTask == FindTask(0L)) {
  353. #ifdef DEBUG
  354.             kprintf("FDU: Master->mn_OwnerTask matches\n");
  355. #endif
  356.             if (Master->mn_WaitList.mlh_TailPred == (struct MinNode *)
  357.                &Master->mn_WaitList.mlh_Head) {
  358. #ifdef DEBUG
  359.                 kprintf("Removing this Master from MasterList\n");
  360. #endif
  361.                 Remove((struct Node *)Master);
  362.                 MyFreeVec((char *)Master);
  363.             } else {
  364. #ifdef DEBUG
  365.                 kprintf("Setting Master->mn_OwnerTask to NULL\n");
  366. #endif
  367.                 Master->mn_OwnerTask = (struct Task *)NULL;
  368.                 Master->mn_OwnerName = (UBYTE *)NULL;
  369.             }
  370.         }
  371.     }
  372.  
  373.     ReleaseSemaphore(&MasterListSema);
  374.     return;
  375. }
  376.  
  377. /* NameDevUnit() will allow the owner of a node to change the owner
  378.    name that is returned when an AttemptDevUnit() times out and fails.
  379.    Useful for programs that sit on the modem and start others when
  380.    someone calls in (i.e., "Getty" becomes "Getty started <program name>").
  381. */
  382.  
  383. void __saveds __asm NameDevUnit(register __a0 UBYTE *Device,
  384.     register __d0 ULONG Unit, register __a1 UBYTE *OwnerName)
  385. {
  386.     MasterListNode_t *Master;
  387.  
  388. #ifdef DEBUG
  389.     kprintf("NameDevUnit called \"%s\" %ld \"%s\"\n", Device,
  390.         Unit, OwnerName);
  391. #endif
  392.  
  393.     if (!Device)
  394.         return;
  395.  
  396.     ObtainSemaphore(&MasterListSema);
  397.  
  398.     if (Master = FindMasterNode(Device, Unit)) {
  399.         if (Master->mn_OwnerTask == FindTask(0L)) {
  400. #ifdef DEBUG
  401.             kprintf("NDU: Master owned by this task.  Setting name\n");
  402. #endif
  403.             Master->mn_OwnerName = OwnerName;
  404.         }
  405.     }
  406.  
  407.     ReleaseSemaphore(&MasterListSema);
  408. }
  409.  
  410. /* AvailDevUnit() will return the availability status of a node very
  411.    quickly.  This may be preferable over AttemptDevUnit(), which can
  412.    take up 5 seconds.
  413. */
  414.  
  415. BOOL __saveds __asm AvailDevUnit(register __a0 UBYTE *Device,
  416.     register __d0 ULONG Unit)
  417. {
  418.     MasterListNode_t *Master;
  419.  
  420.     if (!Device)
  421.         return FALSE;
  422.  
  423. #ifdef DEBUG
  424.     kprintf("AvailDevUnit called \"%s\" %ld\n", Device, Unit);
  425. #endif
  426.     ObtainSemaphore(&MasterListSema);
  427.  
  428.     /* we simply check to see if a node for this device/unit
  429.        exists.  if it does, we return FALSE (not available)
  430.        because it is either owned or someone else will camp
  431.        on it very soon. */
  432.     Master = FindMasterNode(Device, Unit);
  433.  
  434.     ReleaseSemaphore(&MasterListSema);
  435.  
  436.     if (Master)
  437.         return FALSE;
  438.     else
  439.         return TRUE;
  440. }
  441.  
  442. /* OwnerDevUnit() will return the name of the current owner of a particular
  443.    lock.  If the device/unit pair isn't locked, we return ODUERR_UNKNOWN */
  444.  
  445. UBYTE *__saveds __asm OwnerDevUnit(register __a0 UBYTE *Device,
  446.     register __d0 ULONG Unit)
  447. {
  448.     MasterListNode_t *Master;
  449.     STRPTR Owner = NULL;
  450.  
  451.     if (!Device)
  452.         return ODUERR_UNKNOWN;
  453.  
  454. #ifdef DEBUG
  455.     kprintf("OwnerDevUnit called \"%s\" %ld\n", Device, Unit);
  456. #endif
  457.     ObtainSemaphore(&MasterListSema);
  458.  
  459.     Master = FindMasterNode(Device, Unit);
  460.  
  461.     if (Master)
  462.         Owner = Master->mn_OwnerName;
  463.  
  464.     ReleaseSemaphore(&MasterListSema);
  465.  
  466.     if (Owner)
  467.         return Owner;
  468.     else
  469.         return ODUERR_UNKNOWN;
  470. }
  471.  
  472. /* RexxQuery function handles ARexx requests when OwnDevUnit.library has
  473.    been added as an ARexx function host */
  474.  
  475. ULONG __saveds __asm RexxQuery(register __a0 struct RexxMsg *RMsg,
  476.                   register __a1 UBYTE **ReturnArgString)
  477. {
  478.     UBYTE *Error = NULL;
  479.     UBYTE NumArgs;
  480.     BOOL  DidAFunc = FALSE;
  481.     BOOL  WeDidALock = FALSE;
  482.     BOOL  NoRexxLib = FALSE;
  483.  
  484.     ULONG Unit;
  485.     UBYTE NotifyBit;
  486.  
  487.     struct RxsLib *RexxSysBase;
  488.  
  489. #ifdef DEBUG
  490.     kprintf("RexxQuery called\n");
  491. #endif
  492.  
  493.     /* set ReturnArgString to NULL so that if we return early
  494.        ARexx will know it doesn't point at anything */
  495.     *ReturnArgString = NULL;
  496.  
  497.     /* grab the number of arguments they passed us */
  498.     NumArgs = RMsg->rm_Action & 0xFF;
  499.  
  500.     /* now look and see if this is a function we can do.  Note that
  501.        we don't use a lookup table here as we have very few funcs
  502.            to implement and this is easier... signed, his laziness */
  503.  
  504.     if (!stricmp(RMsg->rm_Args[0], "LockDevUnit")) {
  505.         if (NumArgs == 3) {
  506.             /* this is a LockDevUnit call */
  507.             Unit = atoi(RMsg->rm_Args[2]);
  508.             NotifyBit = atoi(RMsg->rm_Args[3]);
  509.             Error = LockDevUnit(RMsg->rm_Args[1], Unit,
  510.                         "ARexx", NotifyBit);
  511.             WeDidALock = DidAFunc = TRUE;
  512.         } else
  513.             return WRONG_NUM_OF_ARGS;
  514.     } else
  515.     if (!stricmp(RMsg->rm_Args[0], "AttemptDevUnit")) {
  516.         if (NumArgs == 3) {
  517.             /* this is an AttemptDevUnit call */
  518.             Unit = atoi(RMsg->rm_Args[2]);
  519.             NotifyBit = atoi(RMsg->rm_Args[3]);
  520.             Error = AttemptDevUnit(RMsg->rm_Args[1], Unit,
  521.                            "ARexx", NotifyBit);
  522.             WeDidALock = DidAFunc = TRUE;
  523.         } else
  524.             return WRONG_NUM_OF_ARGS;
  525.     } else
  526.     if (!stricmp(RMsg->rm_Args[0], "FreeDevUnit")) {
  527.         if (NumArgs == 2) {
  528.             /* this is a FreeDevUnit call */
  529.             Unit = atoi(RMsg->rm_Args[2]);
  530.             FreeDevUnit(RMsg->rm_Args[1], Unit);
  531.             DidAFunc = TRUE;
  532.         } else
  533.             return WRONG_NUM_OF_ARGS;
  534.     } else
  535.     if (!stricmp(RMsg->rm_Args[0], "AvailDevUnit")) {
  536.         if (NumArgs == 2) {
  537.             /* this is an AvailDevUnit call */
  538.             Unit = atoi(RMsg->rm_Args[2]);
  539.             if (AvailDevUnit(RMsg->rm_Args[1], Unit))
  540.                 Error = "1";
  541.             else
  542.                 Error = "0";
  543.             DidAFunc = TRUE;
  544.         } else
  545.             return WRONG_NUM_OF_ARGS;
  546.     } else
  547.     if (!stricmp(RMsg->rm_Args[0], "OwnerDevUnit")) {
  548.         if (NumArgs == 2) {
  549.             /* this is an OwnerDevUnit call */
  550.             Unit = atoi(RMsg->rm_Args[2]);
  551.             Error = OwnerDevUnit(RMsg->rm_Args[1], Unit);
  552.             DidAFunc = TRUE;
  553.         } else
  554.             return WRONG_NUM_OF_ARGS;
  555.     }
  556.  
  557.     if (!DidAFunc)
  558.         return 1L;    /* not a function we recognize */
  559.  
  560.     /* if we get here then we did a function.  try an allocate an
  561.        argstring to return the result string in.  if that fails
  562.        then check if we did a lock.  if we did, and it was succesful
  563.        then we want to free the lock before returning an error saying
  564.        we couldn't allocate the argstring. */
  565.  
  566.     /* try and get rexxsyslib.library */
  567.     if (RexxSysBase = (struct RxsLib *)OpenLibrary("rexxsyslib.library", 0)) {
  568.         {
  569.             ULONG Length;
  570.  
  571.             if (Error)
  572.                 Length = strlen(Error);
  573.             else
  574.                 Length = 0;
  575.  
  576.             *ReturnArgString = CreateArgstring(Error, Length);
  577.         }
  578.  
  579.         CloseLibrary((struct Library *)RexxSysBase);
  580.  
  581.         if (*ReturnArgString)
  582.             return 0L;    /* no low level error so return argstring */
  583.     } else
  584.         NoRexxLib = TRUE;
  585.  
  586.     /* if we did a lock successfully then free it before returning */
  587.     if (WeDidALock && Error == NULL)
  588.         FreeDevUnit(RMsg->rm_Args[1], Unit);
  589.  
  590.     if (NoRexxLib)
  591.         return NO_REXX_LIB;
  592.     else
  593.         return NO_MEM_FOR_ARGSTRING;
  594. }
  595.  
  596. /***************************************************************************/
  597. /***************************************************************************/
  598. /**     These are the support routines used by the top level functions    **/
  599. /***************************************************************************/
  600. /***************************************************************************/
  601.  
  602. /* DoDevUnit() is by in large the most important support routine.  it is
  603.    what actually handles the LockDevUnit() and AttemptDevUnit() requests.
  604.    The difference in the above two calls is the MaxAttempts and
  605.    AttemptDelay that they use.
  606.  
  607.    LockDevUnit() passes -1 in MaxAttempts.  It would take near eternity
  608.    to reach $FFFFFFFF so LockDevUnit() basically blocks forever.  It uses
  609.    an AttemptDelay of 3 seconds to keep from hammering the list constantly.
  610.  
  611.    AttemptDevUnit() passes 5 in MaxAttempts.  Since AttemptDelay is 1
  612.    second for AttemptDevUnit(), this effectively means that this call will
  613.    wait no longer than five seconds for a lock and will return the name
  614.    of the current owner if it can't get it within time.
  615. */
  616.  
  617. UBYTE *DoDevUnit(UBYTE *Device, ULONG Unit, UBYTE *OwnerName,
  618.     UBYTE NotifyBit, ULONG MaxAttempts, ULONG AttemptDelay)
  619. {
  620.     MasterListNode_t *Master;
  621.     WaitListNode_t WaitNode;
  622.  
  623.     struct MsgPort ReplyPort;
  624.     struct timerequest TimeRequest;
  625.  
  626.     UBYTE AttemptNumber;
  627.     UBYTE ReSignalCount;
  628.  
  629.     UBYTE *Error = NULL;
  630.  
  631. #ifdef DEBUG
  632.     kprintf("DoDevUnit called \"%s\" %ld \"%s\" %ld %ld %ld\n",
  633.         Device, Unit, OwnerName, NotifyBit, MaxAttempts, AttemptDelay);
  634. #endif
  635.  
  636.     if (!Device || !*BaseName(Device))
  637.         return ODUERR_BADNAME;
  638.  
  639.     if (NotifyBit == -1)
  640.         return ODUERR_BADBIT;
  641.  
  642.     ObtainSemaphore(&MasterListSema);
  643.  
  644.     /* if the node doesn't exist then we try and create it */
  645.     if (!(Master = FindMasterNode(Device, Unit))) {
  646.         if (Master = CreateMasterNode(Device, Unit))
  647.             SetMasterNode(Master, OwnerName, NotifyBit);
  648.         else
  649.             Error = ODUERR_NOMEM;
  650.  
  651.         ReleaseSemaphore(&MasterListSema);
  652.  
  653.         return Error;
  654.     }
  655.  
  656.     /* if we already own this task then simply return that the
  657.        device is successfully locked.  */
  658.     if (Master->mn_OwnerTask == FindTask(0L)) {
  659. #ifdef DEBUG
  660.         kprintf("DDU: Task already owns this Master\n");
  661. #endif
  662.         ReleaseSemaphore(&MasterListSema);
  663.         return (UBYTE *)NULL;
  664.     }
  665.  
  666.     /* we are going to need the timer to do things right so open
  667.        it up.  If we can't init the timer for use then return
  668.        the appropriate lock failed message */
  669.     if (!OpenTimer(&TimeRequest, &ReplyPort)) {
  670.         ReleaseSemaphore(&MasterListSema);
  671.         return ODUERR_NOTIMER;
  672.     }
  673.  
  674.     /* setup up our wait node and add it onto the wait list */
  675.     WaitNode.wn_Task = FindTask(0L);
  676.     AddTail((struct List *)&Master->mn_WaitList, (struct Node *)&WaitNode);
  677.  
  678.     /* if the current owner has provided a NotifyBit then we
  679.        signal the task to let it know someone wants the list */
  680.     if (Master->mn_OwnerTask && Master->mn_NotifyBit) {
  681. #ifdef DEBUG
  682.         kprintf("DDU: Notifying %08lx on SigBit %ld\n",
  683.             Master->mn_OwnerTask, Master->mn_NotifyBit);
  684. #endif
  685.         Signal(Master->mn_OwnerTask, 1L << Master->mn_NotifyBit);
  686.     }
  687.  
  688.     /* now we wait for the node to become free (mn_OwnerTask = NULL)
  689.        and then check to see if we are the head of the wait list.
  690.        if we are the head then we can own the device.  else someone
  691.        else gets to use it before us so continue waiting.
  692.  
  693.        we must release the MasterListSema and wait before checking
  694.        or the owner will never be able to unlock the node! */
  695.     ReleaseSemaphore(&MasterListSema);
  696.  
  697.     Sleep(&TimeRequest, &ReplyPort, 1L);    /* our initial wait is very short */
  698.  
  699.     for (AttemptNumber = ReSignalCount = 0; AttemptNumber < MaxAttempts;
  700.         ReSignalCount++, AttemptNumber++) {
  701.         ObtainSemaphore(&MasterListSema);
  702.  
  703.         if (Master->mn_OwnerTask) {    /* still owned by someone */
  704.             if (ReSignalCount > RE_SIGNAL) {
  705.                 if (Master->mn_NotifyBit)
  706.                     Signal(Master->mn_OwnerTask,
  707.                         1L << Master->mn_NotifyBit);
  708.                 ReSignalCount = 0;
  709.             }
  710.  
  711.             ReleaseSemaphore(&MasterListSema);
  712.             Sleep(&TimeRequest, &ReplyPort, AttemptDelay);
  713.             continue;
  714.         }
  715.  
  716.         /* device is free.  if we aren't the head of the wait
  717.            list we must let someone else go first */
  718.         if (Master->mn_WaitList.mlh_Head != (struct MinNode *)&WaitNode) {
  719.             ReleaseSemaphore(&MasterListSema);
  720.             Sleep(&TimeRequest, &ReplyPort, AttemptDelay);
  721.             continue;
  722.         }
  723.  
  724.         /* we can own the node!  yeah! */
  725.         RemHead((struct List *)&Master->mn_WaitList);
  726.         SetMasterNode(Master, OwnerName, NotifyBit);
  727.  
  728.         /* if this new owner supplied a NotifyBit and there are
  729.            other people waiting on this node then it is only
  730.            fair to trigger the NotifyBit. */
  731.         if (Master->mn_WaitList.mlh_TailPred != (struct MinNode *)
  732.            &Master->mn_WaitList.mlh_Head)
  733.             if (NotifyBit)
  734.                 Signal(FindTask(0L), 1L << NotifyBit);
  735.  
  736.         /* finally, shut things down and return a successful lock */
  737.         ReleaseSemaphore(&MasterListSema);
  738.         CloseTimer(&TimeRequest, &ReplyPort);
  739.  
  740.         return (UBYTE *)NULL;
  741.     }
  742.  
  743.     /* if we get here then someone else has the device and refuses to
  744.        give it up.  so we return a pointer to the owner's name.
  745.            we also need to remove our WaitNode from the Master's waitlist. */
  746.  
  747.     ObtainSemaphore(&MasterListSema);
  748.  
  749.     Remove((struct Node *)&WaitNode);
  750.  
  751.     Error = Master->mn_OwnerName;
  752.  
  753.     if (!Error)
  754.         Error = ODUERR_UNKNOWN;
  755.  
  756.     ReleaseSemaphore(&MasterListSema);
  757.     CloseTimer(&TimeRequest, &ReplyPort);
  758.  
  759.     return Error;
  760. }
  761.  
  762. /* attempt to find a node in the master list.  returns NULL if not found.
  763.    does not arbitrate for access to the master list.  This must be done
  764.    by the caller! */
  765. MasterListNode_t *FindMasterNode(UBYTE *Device, ULONG Unit)
  766. {
  767.     MasterListNode_t *MasterNode;
  768.  
  769.     Device = BaseName(Device);
  770.  
  771.     for (MasterNode = (MasterListNode_t *)MasterList.mlh_Head;
  772.         MasterNode->mn_Node.mln_Succ; MasterNode = (MasterListNode_t *)
  773.         MasterNode->mn_Node.mln_Succ) {
  774.  
  775. #ifdef DEBUG
  776.         kprintf("FMN: compare \"%s\" %ld with requested \"%s\" %ld\n",
  777.             MasterNode->mn_Device, MasterNode->mn_Unit, Device, Unit);
  778. #endif
  779.         if (strcmp(MasterNode->mn_Device, Device) == 0 &&
  780.             MasterNode->mn_Unit == Unit) {
  781. #ifdef DEBUG
  782.             kprintf("FMN: Match found Master = %08lx\n", MasterNode);
  783. #endif
  784.             return MasterNode;
  785.         }
  786.     }
  787.  
  788.     return (MasterListNode_t *)NULL;
  789. }
  790.  
  791. /* this will try to create a new MasterListNode.  returns NULL if it
  792.    fails (mainly due to an out of memory error).  does not arbitrate
  793.    for access to the master list */
  794. MasterListNode_t *CreateMasterNode(UBYTE *Device, ULONG Unit)
  795. {
  796.     MasterListNode_t *MasterNode;
  797.     ULONG DevNameSize;
  798.     ULONG AllocSize;
  799.  
  800. #ifdef DEBUG
  801.     kprintf("CreateMasterNode called \"%s\" %ld\n", Device, Unit);
  802. #endif
  803.  
  804.     /* failsafe:  if the node already exists then return a
  805.        pointer to it instead of creating a new one. */
  806.  
  807.     if (MasterNode = FindMasterNode(Device, Unit))
  808.         return MasterNode;
  809.  
  810.     /* only store the basename of the device */
  811.     Device = BaseName(Device);
  812.  
  813.     /* alloc some memory to hold the node.  return NULL if the
  814.        alloc fails. we copy the device name onto the end of the
  815.        node so that an original owner can quit and we don't
  816.        lose the device name */
  817.  
  818.     DevNameSize = strlen(Device);
  819.     AllocSize = sizeof(MasterListNode_t) + DevNameSize;
  820.  
  821. #ifdef DEBUG
  822.     kprintf("CMN: DevNameSize = %ld\tAllocSize = %ld\n", DevNameSize, AllocSize);
  823. #endif
  824.  
  825.     if (!(MasterNode = (MasterListNode_t *)MyAllocVec(AllocSize)))
  826.         return (MasterListNode_t *)NULL;
  827.  
  828.     /* copy device name over and fill in a few fields */
  829.  
  830.     MasterNode->mn_SizeOf = AllocSize;
  831.     MasterNode->mn_Unit = Unit;
  832.     CopyMem(Device, MasterNode->mn_Device, DevNameSize + 1);
  833.     NewList((struct List *)&MasterNode->mn_WaitList);
  834.  
  835.     /* add the master node to the master list */
  836.     AddTail((struct List *)&MasterList, (struct Node *)MasterNode);
  837.  
  838.     /* return a pointer to the newly added node */
  839.  
  840. #ifdef DEBUG
  841.     kprintf("CMN: Master %08lx created\n", MasterNode);
  842. #endif
  843.  
  844.     return MasterNode;
  845. }
  846.  
  847. void __inline SetMasterNode(MasterListNode_t *Node, UBYTE *OwnerName, UBYTE NotifyBit)
  848. {
  849. #ifdef DEBUG
  850.     kprintf("SetMasterNode called Master %08lx \"%s\" %ld\n", Node, OwnerName, NotifyBit);
  851. #endif
  852.     
  853.     Node->mn_OwnerTask = FindTask(0L);
  854.     Node->mn_OwnerName = OwnerName;
  855.     Node->mn_NotifyBit = NotifyBit;
  856. }
  857.  
  858. /* find the filename part of a full path spec */
  859. UBYTE * __inline BaseName(UBYTE *Path)
  860. {
  861.     register UBYTE *Ptr;
  862.     register UBYTE Char;
  863.  
  864.     Ptr = Path;
  865.  
  866.     while (Char = *Ptr++)
  867.         if (Char == '/' || Char == ':')
  868.             Path = Ptr;
  869.  
  870.     return Path;
  871. }
  872.  
  873. /***************************************************************************/
  874. /***************************************************************************/
  875. /**        These routines manage the timer.device for doing delays        **/
  876. /***************************************************************************/
  877. /***************************************************************************/
  878.  
  879. BOOL OpenTimer(struct timerequest *TimeRequest, struct MsgPort *ReplyPort)
  880. {
  881.     ULONG SigBit;
  882.  
  883.     memset((char *)TimeRequest, 0, sizeof(struct timerequest));
  884.     memset((char *)ReplyPort, 0, sizeof(struct MsgPort));
  885.  
  886.     if ((SigBit = AllocSignal(-1L)) == -1L)
  887.         return FALSE;
  888.  
  889.     ReplyPort->mp_Node.ln_Type = NT_MSGPORT;
  890.  
  891.     ReplyPort->mp_Flags   = PA_SIGNAL;
  892.     ReplyPort->mp_SigBit  = SigBit;
  893.     ReplyPort->mp_SigTask = FindTask(0L);
  894.  
  895.     NewList(&(ReplyPort->mp_MsgList));
  896.  
  897.     if (OpenDevice(TIMERNAME, UNIT_VBLANK, (struct IORequest *)
  898.         TimeRequest, 0)) {
  899.         FreeSignal(SigBit);
  900.         ReplyPort->mp_SigTask         = (struct Task *) -1;
  901.         ReplyPort->mp_MsgList.lh_Head = (struct Node *) -1;
  902.         return FALSE;
  903.     }
  904.  
  905.     TimeRequest->tr_node.io_Message.mn_ReplyPort = ReplyPort;
  906.  
  907.     return TRUE;
  908. }
  909.  
  910. void CloseTimer(struct timerequest *TimeRequest, struct MsgPort *ReplyPort)
  911. {
  912.     CloseDevice((struct IORequest *)TimeRequest);
  913.     FreeSignal(ReplyPort->mp_SigBit);
  914.     ReplyPort->mp_SigTask         = (struct Task *) -1;
  915.     ReplyPort->mp_MsgList.lh_Head = (struct Node *) -1;
  916. }
  917.  
  918. void __inline Sleep(struct timerequest *TimeRequest, struct MsgPort *ReplyPort, ULONG Seconds)
  919. {
  920.     TimeRequest->tr_node.io_Command = TR_ADDREQUEST;
  921.     TimeRequest->tr_time.tv_secs = Seconds;
  922.     TimeRequest->tr_time.tv_micro = 0;
  923.  
  924.     TimeRequest->tr_node.io_Message.mn_ReplyPort = ReplyPort;
  925.  
  926.     DoIO((struct IORequest *)TimeRequest);
  927. }
  928.